package it.unipi.di.rasip.protocol;



import gov.nist.core.ParseException;
import gov.nist.siplite.address.URI;
import gov.nist.siplite.parser.URLParser;
import it.unipi.di.rasip.utils.RaSipUtils;
import it.unipi.di.rasip.utils.RasipConstant;

import java.io.IOException;

import javax.microedition.io.Connector;
import javax.microedition.sip.SipAddress;
import javax.microedition.sip.SipClientConnection;
import javax.microedition.sip.SipConnectionNotifier;
import javax.microedition.sip.SipDialog;
import javax.microedition.sip.SipException;
import javax.microedition.sip.SipServerConnection;
import javax.microedition.sip.SipServerConnectionListener;

/** La porta selezionata in input (presente nell'uri) è solo un 'suggerimento' la bind viene 
 * fatta sulla prima disponibile, compresa la suggerita; il massimo è 10.
 * 
 * @author paolo
 *
 */
public class SipServer extends Thread implements SipServerConnectionListener {
	private static int portStep = 0;	// usata per fare la bind del server sulle porte successive
	private String uri;
	SipConnectionNotifier scn;
	
	public SipConnectionNotifier getScn() {
		return scn;
	}

	private static SipServer _instance = null;
	
	public static SipServer getInstance () {
		if (_instance == null) {
			try {
				_instance = new SipServer("sip:5080");
			} catch (RasipException e) {
				e.printStackTrace();
			}
		}
		return _instance;
	}
	
	
	private SipServer(String uri) throws RasipException{
		this.uri = uri;
		bind(uri);
	}
	
	
	
	/** Esegue la bind sulla porta selezionata nell'uri. Nel caso essa non sia dispobinibile
	 * prova con la successiva. Esegue questa operazione un massimo di 10 volte dopo cui
	 * solleva un'eccezione di tipo RasipException
	 * @param uri
	 * @throws RasipException
	 */
	private void bind ( String uri ) throws RasipException{
		try {
			scn = (SipConnectionNotifier) Connector.open(uri);
			System.out.println("SIP: :"+ scn.getLocalPort());
			scn.setListener(this);
		} catch (Exception e) {
			if (e instanceof gov.nist.siplite.ObjectInUseException) {
				// Catturo eccezioni e cambio la porta
				if (portStep < 10) {	
					portStep++;
					SipAddress sa = new SipAddress ("sip:" + uri);
					sa.setPort( sa.getPort() + 1);
					bind ( sa.getURI() );
				} else {
					throw new RasipException ("SipServer, porte non disponibili");
				}
			} else {
				throw new RasipException ("SipServer: " + e.getClass() + e.getMessage() ); // getClass()); 
			}
			
		}
	}

	/** Handler per la gestione dei messaggi in ingresso
	 * 
	 */
	public void notifyRequest(SipConnectionNotifier scn) {
		int responceCode;
		StringBuffer msg = new StringBuffer();
		try {
			SipServerConnection ssc = scn.acceptAndOpen();
			String method = ssc.getMethod();
			SipAddress saFrom = new SipAddress(ssc.getHeader("From") );
			SipAddress saTo = new SipAddress(ssc.getHeader("To") );
			
			if (method.equals(RasipConstant.MESSAGE)) {
				//ssc.getDialog()
				System.out.println("MESSAGE: " + ssc.getHeader("From") + " --> " + ssc.getHeader("To"));
				RaSipUtils.writeInputStream(ssc.openContentInputStream(), msg );
				
				Mediator.getInstance().receiveMessage( saTo.getURI(), saFrom.getURI() , msg.toString() );
				responceCode = 200;
			} else if (method.equals(RasipConstant.INVITE)) {
				responceCode = 201;	
				Mediator.getInstance().receiveMessage( saTo.getURI(), saFrom.getURI() , RasipConstant.INVITE );	
			} else if (method.equals(RasipConstant.REGISTER)) {
				responceCode = 201;	
				Mediator.getInstance().receiveMessage( saTo.getURI(), saFrom.getURI() , RasipConstant.REGISTER );	
			} else {
				System.err.println("Method not implemented: \"" + method + "\"");
				responceCode = 401;	
			}
			
			ssc.initResponse(responceCode);
			ssc.send();

		} catch (SipException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
}
